/*
** server.c -- a stream socket server demo
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include "../rsa/myRsa.h"
#include "../sha2/sha2.h"
#include "db.h"
#include <sqlite3.h>
#include "../common/common.h"
#include "documentaccess.h"
#include "../log/sec_logger.h"


#define BACKLOG 10	 // how many pending connections queue will hold

void sigchld_handler(int s)
{
	while(waitpid(-1, NULL, WNOHANG) > 0);
}
RSA *
getClientRSA(char * user)
{
	RSA * clientPublic;
	char * data;
	int i;
	FILE * fp;
	
	clientPublic = RSA_new();
	
	data = (char *)getPublicKey(user);
	//printf("Public key data: %s\n", data);
	if((fp = fopen("temp.key","wb"))==NULL)
		return NULL;
	
	for(i = 0; data[i] != 0; i++)
		fputc(data[i], fp);
	
	fclose(fp);
	fp = fopen("temp.key", "rb");
	
	if(!PEM_read_RSAPublicKey(fp, &clientPublic, 0, NULL))
	{
		fprintf(stderr,"Problem in opening public key");
		return NULL;
	}
	
	
	fclose(fp);
	
	return clientPublic;	
}

int
doSendListFile(int fd, char * filter)
{
	ListPacket documents;
	int amount, i;
	t_documents * test;
	MessagePacket msg;
	
	/* Send the ACK_OK FOR THE CLIENT TO WAIT FOR THE LIST */
	msg.response = ACK_OK;
	send(fd, &msg, sizeof(msg),0);
	
	
	recv(fd, &msg, sizeof(msg), 0);
	if(msg.response != ACK_OK)
	{
		printf("error en envio");
	}
	else
		//printf("Llego");
	fflush(stdout);
	sleep(2);
	/* GET FILE LIST */
	//printf("Busco: %s\n", filter);
	test = getDocuments(filter, &amount);
	
	//printf("Cantidad: %d\n", test->size);
	for (i = 0; i < test->size; i++) {
		strcpy(documents.documents[i], test->documents[i]->title);
	}
		
	documents.amount = test->size;
	
	/* SEND IT */
	send(fd, &documents, sizeof(documents), 0);
	//printf("Tamaño: %d\n", sizeof(documents));
	fflush(stdout);
	return 1;	
	
}

int
getSector(int fd, char * filename, char * retSector)
{
	t_privileges * sectors;
	MessagePacket msg, sector;
	ListPacket list;
	
	sectors = getSectorsForTitle(filename);
	
	if(sectors->size > 1)
	{
		int i;
		/* THE FILE BELONGS TO MORE THAN 1 SECTOR. SO TELL ME THE SECTOR FOR THE FILE */
		msg.response = ACK_WARNING;
		
		send(fd, &msg, sizeof(msg),0);
		
		//printf("sectores a los que pertenece el archivo:\n");
		for (i = 0; i < sectors->size; i++) {
			strcpy(list.documents[i], sectors->sectors[i]->name);
			//printf("%s\n", list.documents[i]);
		}
		list.amount = sectors->size;
	
		/* SEND IT */
		
		send(fd, &list, sizeof(list), 0);
		
		recv(fd, &sector, sizeof(sector), 0);
	
		//msg.response = ACK_OK;
				
		//send(fd, &msg, sizeof(msg),0);
		
		strcpy(retSector, sector.message);
		return 1;
		
	}
	else
	{
		//printf("Uno solo");
		//msg.response = ACK_OK;
		
		//send(fd, &msg, sizeof(msg), 0);
		
		strcpy(retSector,sectors->sectors[0]->name);
		
		return 1;
	}
}

int doSendFile(int fd, char * filename, AuthPacket sessionPacket, char * sector, char * level)
{
	MessagePacket msg;
	ContentPacket fileContent; 
	char * fileData;
	char logData[1024];
	char * auxLevel;
	
	getSector(fd, filename, sector);
	
	auxLevel = getLevelForDocumentSector(filename, sector);
	
	fileContent.fsize = getSize(filename);
	memcpy(fileContent.data, getDocument(filename,sector,auxLevel),fileContent.fsize);
		
	if(!strcmp(fileContent.data, ""))
	{
			sprintf(logData,"The file %s requeried by %s does not exists",filename, sessionPacket.user);
			sec_logAction(logData);
			msg.response = ACK_ERROR;
			strcpy(msg.message, "The file does not exists.");
			send(fd, &msg, sizeof(msg),0);
			return 0;
	}
	
	if(!hasPrivilege(sessionPacket.user, filename,sector))
	{
		/*char * privilegeNeeded;
		privilegeNeeded = privilegeNeeded(filename, sector)*/
		sprintf(logData,"User %s does not have privileges on %s",sessionPacket.user, filename);
		sec_logAction(logData);
		msg.response = ACK_ERROR;	
		sprintf(msg.message, "You don't have privileges to see this file. You are in sector %s",sector);
		send(fd, &msg, sizeof(msg),0);	
		return 0;		
	}
	else
	{
		msg.response = ACK_OK;	
		strcpy(msg.message, "Will send the file.");
		send(fd, &msg, sizeof(msg),0);	
		fileData = getDocument(filename, sector, auxLevel);
	
		if(!strcmp(fileData, ""))
			strcpy(fileContent.fileName,"");
		else
		{
			strcpy(fileContent.data, fileData);
			strcpy(fileContent.fileName, filename);
			strcpy(fileContent.sector, sector);
			fileContent.fsize = getDocumentSize(fileContent.fileName);
			strcpy(fileContent.level, getLevelForDocumentSector(filename, sector));
		}
		
		memcpy(fileContent.sessionDecrypt, getKeyForUserDocument(sessionPacket.user, filename), MAX_RSA_SESSION);
		sprintf(logData,"Usar: %s accesed %s", sessionPacket.user, filename);
		sec_logAction(logData);
		send(fd, &fileContent, sizeof(fileContent), 0);
		strcpy(level, auxLevel);
		return 1;
	}
	
	
	
}

char *
encryptSessionKey(char * user, AuthPacket sessionPacket, char * rsaPass)
{
	RSA * clientPublic, * serverPrivate;
	char * semiDecrypted, * obtained;
	FILE * fp;
	
	if((serverPrivate = (RSA *)privateK_dec(rsaPass))==NULL)
	{
		fprintf(stderr, "Error decrypting private key.\n");
		return NULL;
	}
		
	if((clientPublic=getClientRSA(user))==NULL)
		return NULL;
		
		
	if ((semiDecrypted= publicEncrypt(sessionPacket.sessionKey, clientPublic) ) == NULL)
	{
		fprintf(stderr,"Error encrypting with client's public key.\n");
		fflush(stdout);
	}
			
	if ((obtained = privateEncrypt(semiDecrypted, serverPrivate)) == NULL)
	{
		fprintf(stderr,"Error encrypting with server's private key.");
		fflush(stdout);
	}
		
	return obtained;
}

int makeAccessForUsers(ContentPacket fileContent, AuthPacket sessionPacket, char * rsaString)
{
	t_users * users;
	RSA * userPublicKey;
	char * userPublicKeySessionEncrypted;
	int i;
	
	users = (t_users *)getPrivAboveLevel(fileContent.sector, fileContent.level);
	
	for (i = 0; i < users->size; i++) {
		userPublicKeySessionEncrypted = (char *)encryptSessionKey(users->users[i]->username, sessionPacket, rsaString);
		saveUserDocumentKey(users->users[i]->username,fileContent.fileName, userPublicKeySessionEncrypted);
	}
	return 1;	
}


int doGetFile(int fd, AuthPacket sessionPacket, char * sector, char * level, char * rsaString)
{
	ContentPacket fileContent;
	t_privileges * sectors;
	t_privileges * levels;
	MessagePacket msg;
	ListPacket list;
	char logData[1024];
	int i;
	sectors = getSectorsForUser(sessionPacket.user);
	
	if(sectors==NULL)
		return -1;
	
	if(sectors->size > 1)
	{				
		/* THE USER CAN ACCESS MORE THAN 1 SECTOR. SO TELL ME THE SECTOR FOR THE FILE */
		msg.response = ACK_WARNING;
		send(fd, &msg, sizeof(msg),0);
		for (i = 0; i < sectors->size; i++) {
			strcpy(list.documents[i], sectors->sectors[i]->name);
		}
		list.amount = sectors->size;
		send(fd, &list, sizeof(list), 0);
		recv(fd, &msg, sizeof(msg), 0);
		strcpy(sector, msg.message);	
		levels = getLevelsForSector(sector);
		
			fflush(stdout);
		if(levels->size > 1)
		{
			msg.response = ACK_WARNING;					
			send(fd, &msg, sizeof(msg),0);
			
			for (i = 0; i < levels->size; i++) {
				strcpy(list.documents[i], levels->sectors[i]->name);
			}
			list.amount = levels->size;
			
			send(fd, &list, sizeof(list), 0);
		}
		else
		{
			msg.response = ACK_OK;
			send(fd, &msg, sizeof(msg), 0);
			strcpy(level, levels->sectors[0]->name);
		}
			
		recv(fd, &fileContent, sizeof(fileContent), 0);
		
		if(!strcmp(fileContent.fileName,""))
		{
			sprintf(logData, "The file send by %s exceeds the maximum permited.", sessionPacket.user);
			sec_logAction(logData);
			return -1;
		}			
		msg.response = ACK_OK;
		send(fd, &msg, sizeof(msg),0);	
	}
	else
	{
		
		msg.response = ACK_OK;
		send(fd, &msg, sizeof(msg), 0);
		strcpy(fileContent.sector, sectors->sectors[0]->name);
		levels = getLevelsForSector(fileContent.sector);
				
		if(levels->size > 1)
		{
			msg.response = ACK_WARNING;
			send(fd, &msg, sizeof(msg),0);
				
			for (i = 0; i < levels->size; i++) {
				strcpy(list.documents[i], levels->sectors[i]->name);
			}
			list.amount = levels->size;
				
			send(fd, &list, sizeof(list), 0);
						
			recv(fd, &msg, sizeof(msg), 0);
			if(msg.response != ACK_OK)
				return -1;
		}
		else
		{
			msg.response = ACK_OK;
			send(fd, &msg, sizeof(msg), 0);
			strcpy(level, levels->sectors[0]->name);
		}
		recv(fd, &fileContent, sizeof(fileContent), 0);
		
		if(!strcmp(fileContent.sector, ""))
			strcpy(fileContent.sector, sectors->sectors[0]->name);
		
		if(!strcmp(fileContent.level, ""))
			strcpy(fileContent.level, levels->sectors[0]->name);
	}
	char * nivel;
	nivel = getPrivilegeUserSector(sessionPacket.user, fileContent.sector);
	saveDocument(fileContent.fileName, fileContent.sector, fileContent.level, fileContent.data, fileContent.fsize);
	makeAccessForUsers(fileContent, sessionPacket, rsaString);
		
	if(!hasPrivilege(sessionPacket.user, fileContent.fileName, fileContent.sector))
	{
		sprintf(logData, "The user %s uploaded a file which makes him unable to see it.");
		sec_logAction(logData);
		msg.response = ACK_WARNING;
		
		strcpy(msg.message, "The uploaded file has a privilege which makes you unable to see it.");
	
		send(fd, &msg, sizeof(msg), 0);
	}
	else
	{
		msg.response = ACK_OK;
		send(fd, &msg, sizeof(msg), 0);
	}
	return 1;	
}
int 
doSetPermission(int fd, AuthPacket sessionPacket, char * sector, char * level, char * rsaPass)
{
	MessagePacket msg;
	RSA * serverPrivate, * clientPublic;
	PermPacket permission;
	char * data, * semiDecrypted, * obtained, fileSessionKey[MAX_KEY], * userPublicKeySessionEncrypted;
	char * newSector, * levelList;
	t_privileges * levels;
	int i;
	FILE * fp;
	
	msg.response = ACK_OK;
	send(fd, &msg, sizeof(msg),0);
	recvfrom(fd, &permission, sizeof(permission), 0, NULL, 0);
	strcpy(permission.user, sessionPacket.user);
	
	printf("El usuario %s me pidio que le de permisos a %s en %s\n", sessionPacket.user, permission.alloweduser, permission.fileName );
	fflush(stdout);
		
	if((newSector=malloc(strlen(sessionPacket.user)+ strlen(permission.fileName) +strlen(permission.alloweduser) + 10))==NULL)
			return -1;
			
	sprintf(newSector, "%s_%s_%s", permission.alloweduser, permission.fileName, sector);
	printf("Creo un nuevo sector: %s\n", newSector);
	fflush(stdout);
	addSector(newSector, newSector);
	printf("Lo cree y ahora le voy a dar permisos al usuario pedido en ese sector\n");
	fflush(stdout);
	addPrivilege(permission.alloweduser, newSector, newSector);
	printf("Le di. Ahora voy a dar permisos al documento en ese sector\n");
	fflush(stdout);
	setDocumentLevel(permission.fileName, newSector, newSector);	
	printf("Todo seteado\n");
	fflush(stdout);
	printf("Ahora encripto todo para el usuario nuevo\n");
	fflush(stdout);
	if((serverPrivate = (RSA *)privateK_dec(rsaPass))==NULL)
	{
		fprintf(stderr, "Error decrypting private key.\n");
		return -1;
	}
		
	if((clientPublic=getClientRSA(sessionPacket.user))==NULL)
		return -1;
		
	if ((semiDecrypted= publicDecrypt(permission.sessionKey, clientPublic) ) == NULL)
	{
			printf("errorPuD");
			fflush(stdout);
	}
		
	if ((obtained = privateDecrypt(semiDecrypted, serverPrivate)) == NULL)
	{
			printf("Error.PrD");
			fflush(stdout);
	}
	fflush(stdout);
	strcpy(sessionPacket.sessionKey, obtained);
	
	userPublicKeySessionEncrypted = (char *)encryptSessionKey(permission.alloweduser, sessionPacket, rsaPass);
	saveUserDocumentKey(permission.alloweduser,permission.fileName, userPublicKeySessionEncrypted);
	printf("Ya lo encripte y te mando el ack\n");
	fflush(stdout);	
	msg.response = ACK_OK;
	strcpy(msg.message,"");
	send(fd, &msg, sizeof(msg),0);
	printf("termine\n");
	return 1;
}

int
getSessionKey(int fd, AuthPacket * sessionPacket, char * rsaPass)
{
	RSA * clientPublic, * serverPrivate;
	char buff[MAX_SESSION], * semiDecrypted, *obtained;
	
	FILE * fp;
		
	//printf("RSA pass:%s", rsaPass);
	if((serverPrivate = (RSA *)privateK_dec(rsaPass))==NULL)
	{
		fprintf(stderr, "Error decrypting private key.\n");
		return -1;
	}
	
	recv(fd, sessionPacket, sizeof(*sessionPacket), 0);	
	
	if((clientPublic=getClientRSA(sessionPacket->user))==NULL)
		return -1;
	
	if ((semiDecrypted= publicDecrypt(sessionPacket->sessionKey, clientPublic) ) == NULL)
	{
			printf("errorPuD");
			fflush(stdout);
	}
		
	if ((obtained = privateDecrypt(semiDecrypted, serverPrivate)) == NULL)
	{
			printf("Error.PrD");
			fflush(stdout);
	}

	strcpy(sessionPacket->sessionKey, obtained);
	
	if ((semiDecrypted= publicDecrypt(sessionPacket->password, clientPublic) ) == NULL)
			printf("errorPuD");
		
	if ((obtained = privateDecrypt(semiDecrypted, serverPrivate)) == NULL)
			printf("Error.PrD");	
	
	strcpy(sessionPacket->password, obtained);
	
	return 1;
}

int ServerAction(int fd, char * rsaString)	
{
	RequestPacket request;
	AuthPacket sessionPacket;
	char logData[1024];
	
	struct timeval tv;
	char clientSector[MAX_SECTOR + 1], clientLevel[MAX_LEVEL + 1], * pwd;
	
		
	if(getSessionKey(fd, &sessionPacket, rsaString) == -1)
		return -1;
	
	if(doAuthentication(fd, sessionPacket) == -1)
	{	
		sprintf("Authentication failed by %s", sessionPacket.user);		
		sec_logAction(logData);
		return 0;
	}
	

	while(1)
	{
		recv(fd, &request, sizeof(request),0 );
		//printf("Llego request: %d\n", request.request);
		fflush(stdout);
		switch(request.request)
		{		
			case LIST_FILES: doSendListFile(fd, request.fileName); break;
			case GET_FILE: doSendFile(fd, request.fileName, sessionPacket, clientSector, clientLevel); break;
			case SEND_FILE: doGetFile(fd, sessionPacket, clientSector, clientLevel, rsaString); break;
			case SET_PERMISSION: doSetPermission(fd, sessionPacket, clientSector, clientLevel, rsaString); break;
		}
		if(request.request != GET_FILE || request.request != LIST_FILES)
			kill(getpid(),1);
	}
	
	return 1;
}
int doAuthentication(int fd, AuthPacket sessionPacket)
{
	MessagePacket resp;
	char digest[SHA256_DIGEST_SIZE+1],digest2[SHA256_DIGEST_SIZE+1];
	

	sha256(sessionPacket.password,strlen(sessionPacket.password), digest);
	sha2562Ascii(digest2, digest);
	
	
	if(authenticateUser(sessionPacket.user, digest2))
	{
		resp.response=ACK_OK;
		send(fd, &resp, sizeof(resp), 0);
		return 1;
	}
	else
	{
		resp.response=ACK_ERROR;
		strncpy(resp.message, "User authentication failed.",MAX_ERROR);
		send(fd, &resp, sizeof(resp), 0);
		return -1;
	}	
	
	return 1;
	
}

int main(void)
{
	int sockfd, new_fd, i;  // listen on sock_fd, new connection on new_fd
	struct sockaddr_in my_addr;	// my address information
	struct sockaddr_in their_addr; // connector's address information
	socklen_t sin_size;
	struct sigaction sa;
	char sessionKey[4+1]={0};
	char rsaString[17], *pwd;
	int yes=1, flag	;
		
	
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		perror("socket");
		exit(1);
	}

	if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
		perror("setsockopt");
		exit(1);
	}
	
	my_addr.sin_family = AF_INET;		 // host byte order
	my_addr.sin_port = htons(DEFAULT_PORT);	 // short, network byte order
	my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP
	memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);

	if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof my_addr) == -1) {
		perror("bind");
		exit(1);
	}

	if (listen(sockfd, BACKLOG) == -1) {
		perror("listen");
		exit(1);
	}

	sa.sa_handler = sigchld_handler; // reap all dead processes
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = SA_RESTART;
	if (sigaction(SIGCHLD, &sa, NULL) == -1) {
		perror("sigaction");
		exit(1);
	}
	
	do
	{
		flag = 0;
		pwd = getpass("Private Key Password: ");
		if(pwd == NULL || (strlen(pwd) < 4 || strlen(pwd) > 16))
		{
			flag = 1;
			fprintf(stderr,"Your password length is incorrect\nPlease enter a password between 4 and 16 characters\n");
		}
	}
	while(flag);
	strcpy(rsaString, pwd);

	while(1) {  // main accept() loop
		sin_size = sizeof their_addr;
		if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1) {
			perror("accept");
			continue;
		}
		//printf("server: got connection from %s\n",inet_ntoa(their_addr.sin_addr));
		switch(fork())
		{
				case 0:
					(void) close(sockfd);
					exit(ServerAction(new_fd, rsaString));
				default:					
					(void) close(new_fd);
					break;
				case -1:
					break;
					//printf("\nfork: %s\n", strerror(errno));
				}
		}

	return 0;
}

